﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Threading;
using System.Xml.Linq;
using Ionic.Zip;
using System.Windows.Controls;
using System.ComponentModel;

namespace AutoLoL.Updater
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private static string UPDATES_URL = "http://codingquirks.com/apps/autolol/updates/Updates2.xml?nocache=" + DateTime.Now.ToShortDateString();

        private const string AUTOLOL_PATH = "AutoLoL.exe";
        private const string TEMP_UPDATE_PATH = "Update.zip";

        private int _lastDownloadReport = 0;
        private WebClient _webClient;
        private Update _currentUpdate;
        private BackgroundWorker _prepareWorker;
        private BackgroundWorker _installWorker;
        private FrameworkElement[] _elements;

        public MainWindow()
        {
            InitializeComponent();

            _prepareWorker = new BackgroundWorker();
            _prepareWorker.WorkerReportsProgress = true;
            _prepareWorker.DoWork += new DoWorkEventHandler(PrepareWorker_DoWork);
            _prepareWorker.ProgressChanged += new ProgressChangedEventHandler(Worker_ProgressChanged);
            _prepareWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(PrepareWorker_RunWorkerCompleted);

            _webClient = new WebClient();
            _webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(WebClient_DownloadProgressChanged);
            _webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(WebClient_DownloadFileCompleted);

            _installWorker = new BackgroundWorker();
            _installWorker.WorkerReportsProgress = true;
            _installWorker.DoWork += new DoWorkEventHandler(InstallWorker_DoWork);
            _installWorker.ProgressChanged += new ProgressChangedEventHandler(Worker_ProgressChanged);
            _installWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(InstallWorker_RunWorkerCompleted);

            StartPrepare();
        }

        private void StartPrepare()
        {
            if (!_prepareWorker.IsBusy || _webClient.IsBusy || _installWorker.IsBusy)
            {
                ShowElements(WaitStackPanel);
                StatusTextBox.Text = "";
                _prepareWorker.RunWorkerAsync();
            }
        }

        private void PrepareWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                Clean();

                _prepareWorker.ReportProgress(0, "Starting updater...");
                Thread.Sleep(300);

                if (!DoesAutoLoLExist())
                {
                    throw new Exception(string.Format("Unable to find '{0}'", AUTOLOL_PATH));
                }

                if (IsAutoLoLRunning())
                {
                    throw new Exception("AutoLoL appears to be running, please close AutoLoL before updating");
                }

                _prepareWorker.ReportProgress(0, "Retrieving installed version...");
                Thread.Sleep(300);

                Version version = new Version(FileVersionInfo.GetVersionInfo(AUTOLOL_PATH).FileVersion);
                _prepareWorker.ReportProgress(0, "   Installed version: v" + version);
                Thread.Sleep(300);

                _prepareWorker.ReportProgress(0, "Downloading update information...");
                Thread.Sleep(300);

                List<Update> updates = GetUpdates().Where(c => c.Version > version).OrderByDescending(c => c.Version).ToList();
                _prepareWorker.ReportProgress(0, "Checking for latest update...");
                Thread.Sleep(300);

                if (updates.Count > 0)
                {
                    _currentUpdate = updates.First();
                    _prepareWorker.ReportProgress(0, "   Latest version: v" + _currentUpdate.Version);
                    Thread.Sleep(300);

                    _prepareWorker.ReportProgress(0, "Contacting download server...");
                    _elements = new FrameworkElement[] { WaitStackPanel };
                    e.Result = true;
                }
                else
                {
                    _prepareWorker.ReportProgress(0, "No updates are available!");
                    _elements = new FrameworkElement[] { LaunchButton };
                    e.Result = false;
                }
            }
            catch (Exception ex1)
            {
                _elements = new Control[] { RestartButton, LaunchButton };

                _prepareWorker.ReportProgress(0, "An unexpected error occured =(");
                _prepareWorker.ReportProgress(0, "");
                _prepareWorker.ReportProgress(0, "Please try again by restarting the updater or download the latest version from CodePlex (http://autolol.codeplex.com/).");
                _prepareWorker.ReportProgress(0, "We are sorry for the inconvenience.");
                _prepareWorker.ReportProgress(0, "");
                _prepareWorker.ReportProgress(0, "Error report:");
                _prepareWorker.ReportProgress(0, ex1.ToString());

                e.Result = false;
            }
        }

        private void PrepareWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            ShowElements(_elements);

            if (e.Result is bool && (bool)e.Result)
            {
                StartDownload(new Uri(_currentUpdate.Url), TEMP_UPDATE_PATH);
            }
        }

        private void StartDownload(Uri address, string fileName)
        {
            StatusTextBox.Text += "Downloading update..." + Environment.NewLine;
            StatusTextBox.ScrollToEnd();
            Thread.Sleep(300);

            _lastDownloadReport = 0;

            try
            {
                _webClient.DownloadFileAsync(address, fileName);
            }
            catch (Exception ex1)
            {
                StatusTextBox.Text += "An unexpected error occured =(";
                StatusTextBox.Text += "";
                StatusTextBox.Text += "Please try again by restarting the updater or download the latest version from CodePlex (http://autolol.codeplex.com/).";
                StatusTextBox.Text += "We are sorry for the inconvenience.";
                StatusTextBox.Text += "";
                StatusTextBox.Text += "Error report:";
                StatusTextBox.Text += ex1.ToString();
                StatusTextBox.ScrollToEnd();

                ShowElements(RestartButton, LaunchButton);
            }
        }

        private void WebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            if (e.ProgressPercentage > _lastDownloadReport + 20)
            {
                _lastDownloadReport = e.ProgressPercentage;

                StatusTextBox.Text += "   Download progress: " + e.ProgressPercentage + "%" + Environment.NewLine;
                StatusTextBox.ScrollToEnd();
            }
        }

        private void WebClient_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                _installWorker.RunWorkerAsync();
            }
            else
            {
                StatusTextBox.Text += "An unexpected error occured =(";
                StatusTextBox.Text += "";
                StatusTextBox.Text += "Please try again by restarting the updater or download the latest version from CodePlex (http://autolol.codeplex.com/).";
                StatusTextBox.Text += "We are sorry for the inconvenience.";
                StatusTextBox.Text += "";
                StatusTextBox.Text += "Error report:";
                StatusTextBox.Text += e.Error.ToString();
                StatusTextBox.ScrollToEnd();

                ShowElements(RestartButton, LaunchButton);
            }
        }

        private void InstallWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                if (!DoesAutoLoLExist())
                {
                    throw new Exception(string.Format("Unable to find '{0}'", AUTOLOL_PATH));
                }

                if (IsAutoLoLRunning())
                {
                    throw new Exception("AutoLoL appears to be running, please close AutoLoL before updating");
                }

                _installWorker.ReportProgress(0, "Extracting update...");
                Thread.Sleep(300);

                using (ZipFile zipFile = ZipFile.Read(TEMP_UPDATE_PATH))
                {
                    foreach (ZipEntry zipEntry in zipFile)
                    {
                        string fileName = zipEntry.FileName;

                        try
                        {
                            zipEntry.Extract(ExtractExistingFileAction.OverwriteSilently);
                            _installWorker.ReportProgress(0, "   Extracted: " + fileName);
                        }
                        catch (Exception ex2)
                        {
                            _installWorker.ReportProgress(0, "   Error extracting file: " + fileName);
                        }
                    }
                }

                _installWorker.ReportProgress(0, "Cleaning up...");
                Thread.Sleep(300);

                Clean();

                _installWorker.ReportProgress(0, "Update installed!");

                _elements = new Control[] { LaunchButton };
            }
            catch (Exception ex1)
            {
                _installWorker.ReportProgress(0, "An unexpected error occured =(");
                _installWorker.ReportProgress(0, "");
                _installWorker.ReportProgress(0, "Please try again by restarting the updater or download the latest version from CodePlex (http://autolol.codeplex.com/).");
                _installWorker.ReportProgress(0, "We are sorry for the inconvenience.");
                _installWorker.ReportProgress(0, "");
                _installWorker.ReportProgress(0, "Error report:");
                _installWorker.ReportProgress(0, ex1.ToString());

                _elements = new Control[] { LaunchButton, RestartButton };
            }
        }

        private void InstallWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            ShowElements(_elements);
        }

        private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            StatusTextBox.Text += e.UserState.ToString() + Environment.NewLine;
            StatusTextBox.ScrollToEnd();
        }

        private bool DoesAutoLoLExist()
        {
            if (!File.Exists(AUTOLOL_PATH))
            {
                return false;
            }

            return true;
        }

        private bool IsAutoLoLRunning()
        {
            if (Process.GetProcesses().Where(c => c.ProcessName.Equals("AutoLoL")).Count() > 0)
            {
                return true;
            }

            return false;
        }

        private List<Update> GetUpdates()
        {
            XDocument doc = XDocument.Load(UPDATES_URL);
            return doc.Root.Elements("update").Where(c =>
                c.Attribute("version") != null
                && c.Attribute("url") != null).Select(e => new Update()
                {
                    Version = new Version(e.Attribute("version").Value),
                    Url = e.Attribute("url").Value
                }).ToList();
        }

        private void Clean()
        {
            try
            {
                if (File.Exists(TEMP_UPDATE_PATH))
                {
                    File.Delete(TEMP_UPDATE_PATH);
                }
            }
            catch { }
        }

        private void RestartButton_Click(object sender, RoutedEventArgs e)
        {
            StartPrepare();
        }

        private void AutoLoLButton_Click(object sender, RoutedEventArgs e)
        {
            if (File.Exists(AUTOLOL_PATH))
            {
                Process.Start(AUTOLOL_PATH);
                Close();
            }
        }

        private void ShowElements(params UIElement[] elements)
        {
            // Hide all elements
            LaunchButton.Visibility = Visibility.Collapsed;
            RestartButton.Visibility = Visibility.Collapsed;
            WaitStackPanel.Visibility = Visibility.Collapsed;

            // Show elements
            if (elements != null)
            {
                foreach (UIElement element in elements)
                {
                    element.Visibility = Visibility.Visible;
                }
            }
        }
    }
}
